import electron, { remote } from 'electron';
import path from 'path';
import cfg from './config';
import * as util from '../util';
import { send } from './help';
import { checkVersion, checkSame, download } from './download';

const onEvent = (...args) => send('update', ...args);
const app = electron.app || remote.app;
const filename = `${cfg.name}.exe`;
const installerPath = path.join(cfg.dirs.appData, filename);
const name = 'client';

let isBusying;
let installerMd5;
let hasCallUsingElevation;

const dispatchError = (e) => {
  onEvent('error', e, (e.stack || e).toString());
};
const checkForUpdates = ({ urlDownload = cfg.urlDownload, autoDownload = true, ...args } = {}) => {
  if (isBusying) {
    return Promise.resolve({ isBusying });
  }
  isBusying = true;
  onEvent('checking');
  return checkVersion({ isSemver: true, name, oldVersion: cfg.version, urlDownload, ...args })
    .then((data) => {
      if (!data || !data.hasUpdate || !data.files || !data.files[filename]) {
        onEvent('not-available');
        return false;
      }
      onEvent('available');
      if (!autoDownload) {
        return true;
      }
      installerMd5 = data.files[filename];
      const downArgs = {
        fileUrl: `${urlDownload}${name}/${data.version}/${filename}`,
        filePath: installerPath,
        fileMd5: installerMd5,
        threads: 5,
        isLzma: 0,
        ...args,
        onEvent: (type, res) => {
          switch (type) {
            case 'error':
              onEvent('error', res);
              break;
            case 'progress':
              onEvent('progress', res);
              break;
          }
        },
      };
      return download(downArgs).then((res) => {
        const { fileSave, downStatus } = res;
        if (downStatus === 1) {
          util.renameSync(fileSave, installerPath);
          onEvent('replaced', res);
          return 'replaced';
        } else {
          onEvent('same', res);
          return 'same';
        }
      });
    })
    .then((res) => {
      isBusying = false;
      return res;
    })
    .catch((error) => {
      isBusying = false;
      throw error;
    });
};
const quitAndInstall = () => {
  return checkSame({ filePath: installerPath, fileMd5: installerMd5 }).then((isSame) => {
    if (isSame) {
      onEvent('before-quit');
      doInstall();
      setImmediate(() => {
        app.quit();
      });
    }
  });
};
const callUsingElevation = (args) => {
  if (hasCallUsingElevation) {
    return;
  }
  hasCallUsingElevation = true;
  return util
    .spawnDetached(path.join(cfg.appPath, 'resources/elevate.exe'), [installerPath].concat(args))
    .catch((e) => dispatchError(e));
};
const doInstall = () => {
  const args = ['--updated', '--force-run'];
  return util.spawnDetached(installerPath, args).catch((e) => {
    // https://github.com/electron-userland/electron-builder/issues/1129
    // Node 8 sends errors: https://nodejs.org/dist/latest-v8.x/docs/api/errors.html#errors_common_system_errors
    const errorCode = e.code;
    if (errorCode === 'UNKNOWN' || errorCode === 'EACCES') {
      return callUsingElevation(args);
    } else {
      dispatchError(e);
    }
  });
};

export default { checkForUpdates, quitAndInstall };
